﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataAccess = Microsoft.MSE.Catalog.Module.Data;
using DTO = Microsoft.MSE.Catalog.Contracts;
using System.Data.SqlClient;
using System.Data;
using System.Xml;

namespace Microsoft.MSE.Catalog.Module.Business
{
    public class PolicyManager
    {
        public PolicyManager(){}

        public static Guid CreatePolicy(DTO.Policy policy, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;
            //Guid implementationID = Guid.Empty;

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                if (txn == null)
                {
                    txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                    newTransaction = true;
                }

                //Create policy
                policy.ID = DataAccess.Policy.CreatePolicy(policy, txn, conn);

                //Assign Aseertions to policy
                if (policy.PolicyAssertions != null)
                {
                    foreach (DTO.PolicyAssertion assertion in policy.PolicyAssertions)
                    {
                        Guid assertionID = EntityManager.GetIDFromName(assertion.Name, DTO.EntityClass.PolicyAssertion, txn, conn);
                        if (assertionID == Guid.Empty)
                        {
                            assertion.ID = CreatePolicyAssertion(assertion, txn, conn);
                        }
                        AssignPolicyAssertionToPolicy(policy.ID, assertion.ID, assertion.AssertionOrder, txn, conn);
                    }
                }

                if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
            }
            catch (Exception)
            {
                if ((txn != null) && (newTransaction)) txn.Rollback();
                throw;
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close();
            }

            return policy.ID;
        }

        public static void DeletePolicy(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                if (txn == null)
                {
                    txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                    newTransaction = true;
                }
                //Get list of associated assertions
                DTO.PolicyAssertionList assertionList = ListPolicyAssertionsByPolicy(ID, txn, conn);
                if (assertionList != null && assertionList.PolicyAssertions != null)
                {
                    foreach (DTO.PolicyAssertionListElement assertionEle in assertionList.PolicyAssertions)
                    {
                        DataAccess.Policy.RemovePolicyAssertionFromPolicy(ID, assertionEle.ID, txn, conn);
                    }
                }
                DataAccess.Policy.DeletePolicy(ID, txn, conn);

                if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
            }
            catch (Exception)
            {
                if ((txn != null) && (newTransaction)) txn.Rollback();
                throw;
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close();
            }
        }

        public static DTO.Policy UpdatePolicy(DTO.Policy policy, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;
            DTO.Policy rtrnPolicy = null;

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                if (txn == null)
                {
                    txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                    newTransaction = true;
                }

                //Change Tracking
                DTO.RepositoryChangeSet changeSet = ChangeTrackingHelper.GetChangeSetForPolicyUpdate(policy, txn, conn);

                rtrnPolicy = DataAccess.Policy.UpdatePolicy(policy, txn, conn);

                if (policy.PolicyAssertions != null)
                {
                    //Delete all existing assertions for the policy
                    DataAccess.Policy.RemoveAllPolicyAssertionsFromPolicy(policy.ID, txn, conn);

                    XmlDocument policyXML = new XmlDocument();
                    policyXML.LoadXml(CatalogSvcHelper.policyModelXml);
                    XmlNode destinationNode = policyXML.FirstChild as XmlNode;

                    foreach (DTO.PolicyAssertion assertion in policy.PolicyAssertions)
                    {
                        //Assign Aseertions to policy
                        AssignPolicyAssertionToPolicy(policy.ID, assertion.ID, assertion.AssertionOrder, txn, conn); 
                        //Create PolicyXML
                        destinationNode.AppendChild(policyXML.ImportNode(assertion.Assertion as XmlNode, true));
                    }

                    policy.WSPolicy = policyXML.DocumentElement;
                }

                //record ChangeSet
                DataAccess.RepositoryChange.Save(changeSet, txn, conn);

                if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
            }
            catch (Exception)
            {
                if ((txn != null) && (newTransaction)) txn.Rollback();
                throw;
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close();
            }

            return rtrnPolicy;
        }

        public static DTO.Policy GetPolicy(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return CatalogSvcHelper.AssemblePolicyFromPolicyAssertion(ID, txn, conn);
        }

        public static DTO.PolicyList ListPolicies( SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Policy.ListPolicies(txn, conn);
        }

        public static void AssignPolicyAssertionToPolicy(Guid policyID, Guid policyAssertionID, int assertionOrder, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.Policy.AssignPolicyAssertionToPolicy(policyID, policyAssertionID, assertionOrder, txn, conn);
        }

        public static void RemovePolicyAssertionFromPolicy(Guid policyID, Guid policyAssertionID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.Policy.RemovePolicyAssertionFromPolicy(policyID, policyAssertionID, txn, conn);
        }

        #region Policy Assertion functions

        public static Guid CreatePolicyAssertion(DTO.PolicyAssertion policyAssertion, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.PolicyAssertion.CreatePolicyAssertion(policyAssertion, txn, conn);
        }

        public static void DeletePolicyAssertion(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.PolicyAssertion.DeletePolicyAssertion(ID, txn, conn);
        }

        public static DTO.PolicyAssertion UpdatePolicyAssertion(DTO.PolicyAssertion policyAssertion, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;
            DTO.PolicyAssertion returnAssertion = null;

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                if (txn == null)
                {
                    txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                    newTransaction = true;
                }

                //Change Tracking
                DTO.RepositoryChangeSet changeSet = ChangeTrackingHelper.GetChangeSetForPolicyAssertionUpdate(policyAssertion, txn, conn);

                returnAssertion = DataAccess.PolicyAssertion.UpdatePolicyAssertion(policyAssertion, txn, conn);

                //record ChangeSet
                DataAccess.RepositoryChange.Save(changeSet, txn, conn);

                if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
            }
            catch (Exception)
            {
                if ((txn != null) && (newTransaction)) txn.Rollback();
                throw;
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close();
            }

            return returnAssertion;
        }

        public static DTO.PolicyAssertion GetPolicyAssertion(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.PolicyAssertion.GetPolicyAssertion(ID, txn, conn);
        }

        public static DTO.PolicyAssertionList ListPolicyAssertions( SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.PolicyAssertion.ListPolicyAssertions(txn, conn);
        }

        public static DTO.PolicyAssertionList ListPolicyAssertionsByPolicy(Guid policyID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.PolicyAssertion.ListPolicyAssertionsByPolicy(policyID, txn, conn);
        }

        #endregion

        #region Assertion Type functions

        public static Guid CreateAssertionType(DTO.AssertionType assertionType, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.AssertionType.CreateAssertionType(assertionType, txn, conn);
        }

        public static void DeleteAssertionType(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.AssertionType.DeleteAssertionType(ID, txn, conn);
        }

        public static DTO.AssertionType UpdateAssertionType(DTO.AssertionType assertionType, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;
            DTO.AssertionType returnAssertionType = null;

            try
            {
                if (conn == null) conn = CatalogSvcHelper.GetConnection();
                if (conn.State == ConnectionState.Closed)
                {
                    autoclose = true;
                    conn.Open();
                }

                if (txn == null)
                {
                    txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                    newTransaction = true;
                }

                //Change Tracking
                DTO.RepositoryChangeSet changeSet = ChangeTrackingHelper.GetChangeSetForAssertionTypeUpdate(assertionType, txn, conn);

                returnAssertionType =  DataAccess.AssertionType.UpdateAssertionType(assertionType, txn, conn);
                
                //record ChangeSet
                DataAccess.RepositoryChange.Save(changeSet, txn, conn);

                if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
            }
            catch (Exception)
            {
                if ((txn != null) && (newTransaction)) txn.Rollback();
                throw;
            }
            finally
            {
                if (autoclose && (conn != null)) conn.Close();
            }

            return returnAssertionType;

        }

        public static DTO.AssertionType GetAssertionType(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DTO.AssertionType assertionType = DataAccess.AssertionType.GetAssertionType(ID, txn, conn);

            assertionType.AssertionTypeSubjects = GetAssertionTypeSubjects(assertionType.ID, txn, conn);

            return assertionType;
        }

        public static DTO.AssertionTypeList ListAssertionType( SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.AssertionType.ListAssertionType(txn, conn);
        }

        public static void UpdateAssertionTypeSchemaObjectReferences(Guid assertionTypeID, DTO.SchemaObject oldSchemaObject, DTO.SchemaObject newSchemaObject,
                                                                SqlTransaction txn, SqlConnection conn)
        {
            DTO.AssertionType assertionType = GetAssertionType(assertionTypeID,txn,conn);
            if (assertionType.SchemaObject != null && assertionType.SchemaObject.ID == oldSchemaObject.ID)
            {
                assertionType.SchemaObject = newSchemaObject;
            }
            UpdateAssertionType(assertionType,txn,conn);
        }

        #endregion

        #region Policy Group functions

        public static List<DTO.PolicyListElement> ListPoliciesByGroup(Guid entityGroupID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Policy.ListPoliciesByGroup(entityGroupID, txn, conn);
        }

        public static DTO.PolicyGroup CreatePolicyGroup(DTO.PolicyGroup policyGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    policyGroup.ID = DataAccess.Entity.CreateEntityGroup(policyGroup.Name, policyGroup.Description, 8, txn, conn);

                    if (policyGroup.Policies != null)
                    {
                        foreach (DTO.PolicyListElement policy in policyGroup.Policies)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(policyGroup.ID, policy.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return policyGroup;
        }

        public static DTO.PolicyGroup UpdatePolicyGroup(DTO.PolicyGroup policyGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    DataAccess.Entity.UpdateEntityGroup(policyGroup.ID, policyGroup.Name, policyGroup.Description, txn, conn);

                    List<DTO.PolicyListElement> oldList = DataAccess.Policy.ListPoliciesByGroup(policyGroup.ID, txn, conn);
                    /// Delete all items from the old list
                    if (oldList != null)
                    {
                        foreach (DTO.PolicyListElement oldListElement in oldList)
                        {
                            DataAccess.Entity.RemoveEntityFromEntityGroup(policyGroup.ID, oldListElement.ID, txn, conn);
                        }
                    }
                    /// Add all items from the new list
                    if (policyGroup.Policies != null)
                    {
                        foreach (DTO.PolicyListElement newListElement in policyGroup.Policies)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(policyGroup.ID, newListElement.ID, txn, conn);
                        }
                    }


                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return policyGroup;
        }

        public static DTO.PolicyGroup GetPolicyGroup(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DTO.PolicyGroup policyGroup = new DTO.PolicyGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, txn, conn);

            policyGroup.ID = entityGroup.ID;
            policyGroup.Name = entityGroup.Name;
            policyGroup.Description = entityGroup.Description;

            policyGroup.Policies = ListPoliciesByGroup(ID, txn, conn);

            return policyGroup;
        }

        #endregion     

        #region Policy Assertion Group functions

        public static List<DTO.PolicyAssertionListElement> ListPolicyAssertionsByGroup(Guid entityGroupID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.PolicyAssertion.ListPolicyAssertionsByGroup(entityGroupID, txn, conn);
        }

        public static DTO.PolicyAssertionGroup CreatePolicyAssertionGroup(DTO.PolicyAssertionGroup policyAssertionGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    policyAssertionGroup.ID = DataAccess.Entity.CreateEntityGroup(policyAssertionGroup.Name, policyAssertionGroup.Description, 19, txn, conn);

                    if (policyAssertionGroup.PolicyAssertions != null)
                    {
                        foreach (DTO.PolicyAssertionListElement policyAssertion in policyAssertionGroup.PolicyAssertions)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(policyAssertionGroup.ID, policyAssertion.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return policyAssertionGroup;
        }

        public static DTO.PolicyAssertionGroup UpdatePolicyAssertionGroup(DTO.PolicyAssertionGroup policyAssertionGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    DataAccess.Entity.UpdateEntityGroup(policyAssertionGroup.ID, policyAssertionGroup.Name, policyAssertionGroup.Description, txn, conn);

                    List<DTO.PolicyAssertionListElement> oldList = DataAccess.PolicyAssertion.ListPolicyAssertionsByGroup(policyAssertionGroup.ID, txn, conn);
                    /// Delete all items from the old list
                    if (oldList != null)
                    {
                        foreach (DTO.PolicyAssertionListElement oldListElement in oldList)
                        {
                            DataAccess.Entity.RemoveEntityFromEntityGroup(policyAssertionGroup.ID, oldListElement.ID, txn, conn);
                        }
                    }
                    /// Add all items from the new list
                    if (policyAssertionGroup.PolicyAssertions != null)
                    {
                        foreach (DTO.PolicyAssertionListElement newListElement in policyAssertionGroup.PolicyAssertions)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(policyAssertionGroup.ID, newListElement.ID, txn, conn);
                        }
                    }


                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return policyAssertionGroup;
        }

        public static DTO.PolicyAssertionGroup GetPolicyAssertionGroup(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DTO.PolicyAssertionGroup policyAssertionGroup = new DTO.PolicyAssertionGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, txn, conn);

            policyAssertionGroup.ID = entityGroup.ID;
            policyAssertionGroup.Name = entityGroup.Name;
            policyAssertionGroup.Description = entityGroup.Description;

            policyAssertionGroup.PolicyAssertions = ListPolicyAssertionsByGroup(ID, txn, conn);

            return policyAssertionGroup;
        }

        #endregion    

        #region Assertion Type Group functions

        public static List<DTO.AssertionTypeListElement> ListAssertionTypesByGroup(Guid entityGroupID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.AssertionType.ListAssertionTypesByGroup(entityGroupID, txn, conn);
        }

        public static DTO.AssertionTypeGroup CreateAssertionTypeGroup(DTO.AssertionTypeGroup assertionTypeGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    assertionTypeGroup.ID = DataAccess.Entity.CreateEntityGroup(assertionTypeGroup.Name, assertionTypeGroup.Description, 18, txn, conn);

                    if (assertionTypeGroup.AssertionTypes != null)
                    {
                        foreach (DTO.AssertionTypeListElement policyAssertion in assertionTypeGroup.AssertionTypes)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(assertionTypeGroup.ID, policyAssertion.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return assertionTypeGroup;
        }

        public static DTO.AssertionTypeGroup UpdateAssertionTypeGroup(DTO.AssertionTypeGroup assertionTypeGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    DataAccess.Entity.UpdateEntityGroup(assertionTypeGroup.ID, assertionTypeGroup.Name, assertionTypeGroup.Description, txn, conn);

                    List<DTO.AssertionTypeListElement> oldList = DataAccess.AssertionType.ListAssertionTypesByGroup(assertionTypeGroup.ID, txn, conn);
                    /// Delete all items from the old list
                    if (oldList != null)
                    {
                        foreach (DTO.AssertionTypeListElement oldListElement in oldList)
                        {
                            DataAccess.Entity.RemoveEntityFromEntityGroup(assertionTypeGroup.ID, oldListElement.ID, txn, conn);
                        }
                    }
                    /// Add all items from the new list
                    if (assertionTypeGroup.AssertionTypes != null)
                    {
                        foreach (DTO.AssertionTypeListElement newListElement in assertionTypeGroup.AssertionTypes)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(assertionTypeGroup.ID, newListElement.ID, txn, conn);
                        }
                    }


                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return assertionTypeGroup;
        }

        public static DTO.AssertionTypeGroup GetAssertionTypeGroup(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DTO.AssertionTypeGroup assertionTypeGroup = new DTO.AssertionTypeGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, txn, conn);

            assertionTypeGroup.ID = entityGroup.ID;
            assertionTypeGroup.Name = entityGroup.Name;
            assertionTypeGroup.Description = entityGroup.Description;

            assertionTypeGroup.AssertionTypes = ListAssertionTypesByGroup(ID, txn, conn);

            return assertionTypeGroup;
        }

        #endregion    

        #region Entity Policies functions
        //TODO these are obsolete. Need to remove them as part of the sprint.

        public static void AssignEntityPolicy(Guid entityID, Guid policyID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.Policy.AssignEntityPolicy(entityID, policyID, txn, conn);
        }

        public static void DeletePoliciesFromEntity(Guid entityID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.Policy.DeletePoliciesFromEntity(entityID, txn, conn);
        }

        public static DTO.Policy[] GetEntityPolicies(Guid entityID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Policy.GetEntityPolicies(entityID, txn, conn);
        }

        #endregion

        #region AssertionType Subjects functions
        //TODO these are obsolete. Need to remove them as part of the sprint.

        public static void AssignSubjectToAssertionType(DTO.EntityClass entityClass, Guid assertionTypeID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.AssertionType.AssignSubjectToAssertionType(entityClass, assertionTypeID, txn, conn);
        }

        public static void RemoveAllSubjectsFromAssertionType(Guid assertionTypeID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.AssertionType.RemoveAllSubjectsFromAssertionType(assertionTypeID, txn, conn);
        }

        public static DTO.EntityClass[] GetAssertionTypeSubjects(Guid assertionTypeID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.AssertionType.GetAssertionTypeSubjects(assertionTypeID, txn, conn);
        }

        #endregion
    }
}
